/*
 * Copyright 2003 Program of Computer Graphics, Cornell University
 *     580 Rhodes Hall
 *     Cornell University
 *     Ithaca NY 14853
 * Web: http://www.graphics.cornell.edu/
 * 
 * Not for commercial use. Do not redistribute without permission.
 */

#include "stdafx.h"

#include "AxisAlignedBox.h"
#include "BriteDefs.h"
#include <float.h>
#include <stdio.h>


/** If the segment intersects this box, then it changes the start and end parameters
 * of the segment so that it is the intersection of the interior of the box
 * and itself, and the method returns true.
 * If the segment does not intersect the box, false is returned and the ray
 * is unchanged.
 * @return true iff the ray intersected this box
 */
/*
bool AxisAlignedBox::clipRay(Ray *ray) const
{
//  F32vec4 invDir = F32vec4(1.0f)/ray->direction;
  F32vec4 invDir = rcp_nr(ray->direction);
  F32vec4 a = (min-ray->origin)*invDir;
  F32vec4 b = (max-ray->origin)*invDir;
  F32vec4 tEntry = simd_min(a,b);
  F32vec4 tExit = simd_max(a,b);
  float maxEntry = ray->tStart;
  float minExit = ray->tEnd;
  if (ray->dirX()!=0) {
    maxEntry = maxv(maxEntry,tEntry[0]);
    minExit = minv(minExit,tExit[0]);
  } else {
    if ((ray->originX() < min[0])||(ray->originX() > max[0]))
    return false;
  }

  if (ray->dirY()!=0) {
    maxEntry = maxv(maxEntry,tEntry[1]);
    minExit = minv(minExit,tExit[1]);
  } else {
    if ((ray->originY() < min[1])||(ray->originY() > max[1]))
      return false;
  }

  if (ray->dirZ()!=0) {
    maxEntry = maxv(maxEntry,tEntry[2]);
    minExit = minv(minExit,tExit[2]);
  } else {
    if ((ray->originZ() < min[2])||(ray->originZ() > max[2]))
    return false;
  }

  if (maxEntry <= minExit) {
    // The line AND the ray intersect the box
    ray->tStart = maxEntry;
    ray->tEnd = minExit;
    return true;
  }
  // The line does not intersect the box
  return false; //otherwise they do not intersect
}
*/

//compute min of four values, min(a[0],a[1],a[2],*b)
static inline F32vec1 min31(const F32vec4 &a, float *b) {
  F32vec4 v1100 = unpack_low(a,a);
  F32vec4 v321b = _mm_move_ss(a,_mm_load_ss(b));
  F32vec4 temp = simd_min(v1100,v321b);
  F32vec4 temp2 = simd_min(temp,_mm_movehl_ps(F32vec4Zero(),temp));
//  F32vec4 temp2 = simd_min(temp,_mm_shuffle_ps(temp,temp,_MM_SHUFFLE(2,2,2,2)));
  return F32vec1(temp2);
}
//compute max of four values, max(a[0],a[1],a[2],*b)
static inline F32vec1 max31(const F32vec4 &a, float *b) {
  F32vec4 v1100 = unpack_low(a,a);
  F32vec4 v321b = _mm_move_ss(a,_mm_load_ss(b));
  F32vec4 temp = simd_max(v1100,v321b);
  F32vec4 temp2 = simd_max(temp,_mm_movehl_ps(F32vec4Zero(),temp));
//  F32vec4 temp2 = simd_max(temp,_mm_shuffle_ps(temp,temp,_MM_SHUFFLE(2,2,2,2)));
  return F32vec1(temp2);
}

bool AxisAlignedBox::clipRay(Ray *ray) const
{
//  F32vec4 invDir = F32vec4(1.0f)/ray->direction;
  F32vec4 invDir = rcp_nr(ray->direction);
  F32vec4 a = (min-ray->origin)*invDir;
  F32vec4 b = (max-ray->origin)*invDir;
  F32vec4 tEntry = simd_min(a,b);
  F32vec4 tExit = simd_max(a,b);

  //we need to handle the case when a component of ray->direction is zero
  //in which case the computed t values are bad and the proper test is
  //valid iff ray->origin >= min and ray->origin <= max
  F32vec4 dirZeroTest = cmpeq(ray->direction,F32vec4Zero());
  F32vec4 dirZeroValid = cmpge(ray->origin,min) & cmple(ray->origin,max);
  //for zero components we substitute either tStart or tEnd depending on whether
  //that component passed the appropriate test for zero components.  If the component
  //is not zero, then we do not modify that component in tEntry and tExit
  F32vec4 dirZeroMin = Select(F32vec4(ray->tStart),F32vec4(ray->tEnd),dirZeroValid);
  F32vec4 dirZeroMax = Select(F32vec4(ray->tEnd),F32vec4(ray->tStart),dirZeroValid);
  tEntry = Select(dirZeroMin,tEntry,dirZeroTest);
  tExit = Select(dirZeroMax,tExit,dirZeroTest);

  //now compute max of all ray entry points and min of exit points to see
  //if the ray intersected the box
  F32vec1 maxEntry = max31(tEntry,&ray->tStart);
  F32vec1 minExit = min31(tExit,&ray->tEnd);
  if (_mm_ucomile_ss(minExit,maxEntry)) {
    // The ray does not intersect the box
    return false;
  } 
  Store_F32vec1(&ray->tStart,maxEntry);
  Store_F32vec1(&ray->tEnd,minExit);
  return true;
}

inline bool goodFloat(float f) {
  const int goodMask = brite::GOOD_FLOAT_MASK;
  if (f==0) return true;
  if (_fpclass(f)&~goodMask) return false;
  return (absv(f) > 1e-20);
}

bool AxisAlignedBox::clipRayExt(RayExt &ray) const
{
  F32vec4 a = (min-ray.origin)*ray.invDirection;
  F32vec4 b = (max-ray.origin)*ray.invDirection;
  F32vec4 tEntry = simd_min(a,b);
  F32vec4 tExit = simd_max(a,b);
  F32vec1 maxEntry = max31(tEntry,&ray.tStart);
  F32vec1 minExit = min31(tExit,&ray.tEnd);
//  float maxEntry = maxv(maxv(ray.tStart,tEntry[0]),maxv(tEntry[1],tEntry[2]));
//  float minExit = minv(minv(ray.tEnd,tExit[0]),minv(tExit[1],tExit[2]));
  if (_mm_ucomilt_ss(minExit,maxEntry)) {
//  if (minExit < maxEntry) {
    // The ray does not intersect the box
    return false;
  } 
  Store_F32vec1(&ray.tStart,maxEntry);
  Store_F32vec1(&ray.tEnd,minExit);
//  ray.tStart = maxEntry;
//  ray.tEnd = minExit;
  return true;
}

    
